﻿using System;

namespace Bricks.ExceptionHandling
{

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="THandler"></typeparam>
    public abstract class ExceptionHandlerFacadeBase<THandler> : FacadeBase<THandler>, IExceptionHandlerFacade<THandler>
    {

        /// <summary>
        /// 
        /// </summary>
        /// <param name="handlerAccessor"></param>
        protected ExceptionHandlerFacadeBase(Func<THandler> handlerAccessor) :
            base(handlerAccessor)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="exceptionToHandle"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public abstract bool Handle(Exception exceptionToHandle, string key);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="exceptionToHandle"></param>
        /// <param name="key"></param>
        /// <param name="exceptionToThrow"></param>
        /// <returns></returns>
        public bool Handle(Exception exceptionToHandle, string key, out Exception exceptionToThrow)
        {
            try
            {
                bool shouldRethrow = Handle(exceptionToHandle, key);

                exceptionToThrow = null;

                return shouldRethrow;
            }
            catch (Exception exception)
            {
                exceptionToThrow = exception;

                return true;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="action"></param>
        /// <param name="key"></param>
        public void Process(Action action, string key)
        {
            Process<bool>(() =>
            {
                action.Invoke();

                return true;
            }, key);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="action"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public TResult Process<TResult>(Func<TResult> action, string key)
        {
            return Process(action, default(TResult), key);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="action"></param>
        /// <param name="defaultResult"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        public virtual TResult Process<TResult>(Func<TResult> action, TResult defaultResult, string key)
        {
            if (action == null)
            {
                throw new ArgumentNullException("action");
            }
            if (string.IsNullOrWhiteSpace(key))
            {
                throw new ArgumentNullException("key");
            }

            try
            {
                return action.Invoke();
            }
            catch (Exception exception)
            {
                if (Handle(exception, key)) throw;
            }

            return defaultResult;
        }
    }
}
